#include "config.h"

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include <clx/libECM.h>

#include "libms2sim.h"

#define ZETA	2.15
#define SIGMA	1.72

typedef struct {
	ecm_uint32	pos;
	gsl_rng	*rng;
	ecm_double	mu;
	ecm_double	sigma;
	ECMBOOL		param_set;
} istime_t;

msl_istime_t
msl_istime_create(msl_istime_type_t type, unsigned long seed)
{
	istime_t	*istime;

	istime = (istime_t *)ecm_malloc(sizeof(istime_t));
	if (istime == NULL)
		return NULL;
	istime->rng = gsl_rng_alloc(gsl_rng_default);
	if (istime->rng == NULL) {
		ecm_free(istime);
		return NULL;
	}

	istime->mu = ZETA;
	istime->sigma = SIGMA;
	istime->param_set = ECM_FALSE;

	return istime;
}

int
msl_istime_set_param(msl_istime_t msl_istime, double mu, double sigma)
{
	istime_t	*istime = (istime_t *)msl_istime;

	istime->param_set = ECM_TRUE;
	istime->mu = mu;
	istime->sigma = sigma;

	return 1;
}

unsigned int
msl_istime_get_seek(msl_istime_t msl_istime, unsigned int pos, unsigned int length,
		    unsigned int *pistime, int *pdistance)
{
	istime_t	*istime = (istime_t *)msl_istime;
	ecm_uint32	interseek_time;
	ecm_int32	dist;
	ecm_int32	seekpos;
	ECMBOOL		forward;

	if (istime == NULL)
		return 0;

	if (istime->param_set) {
		interseek_time = (ecm_uint32)(gsl_ran_lognormal(istime->rng, istime->mu, istime->sigma) * length);
	}
	else
		interseek_time = (ecm_uint32)(gsl_ran_lognormal(istime->rng, istime->mu, istime->sigma) * length / 50);
	if (pos + interseek_time > length)
		return 0;

	if (gsl_ran_flat(istime->rng, 0, 1) > 0.4)
		forward = ECM_TRUE;
	else
		forward = ECM_FALSE;

	seekpos = pos + interseek_time;
	dist = (ecm_int32)(gsl_ran_lognormal(istime->rng, istime->mu, istime->sigma) * 6);
	if (!forward)
		dist *= (-1);

	if (seekpos + dist <= 1)
		dist = 1 - seekpos;
	else if (seekpos + dist >= (ecm_int32)length)
		dist = length - seekpos;

	*pistime = interseek_time;
	*pdistance = dist;

	return 1;
}

void
msl_istime_free(msl_istime_t msl_istime)
{
	istime_t	*istime = (istime_t *)msl_istime;

	if (istime == NULL)
		return;

	gsl_rng_free(istime->rng);
	ecm_free(istime);
}

msl_istime_type_t
msl_istime_parse_typestr(const char *typestr)
{
	if (ecm_strcmp(typestr, "noseek") == 0)
		return MSL_ISTIME_TYPE_NOSEEK;
	if (ecm_strcmp(typestr, "lognormal") == 0)
		return MSL_ISTIME_TYPE_LOGNORMAL;
	return MSL_ISTIME_TYPE_NONE;
}

const char *
msl_istime_get_typestr(msl_istime_type_t type)
{
	switch (type) {
	case MSL_ISTIME_TYPE_NOSEEK:
		return "noseek";
	case MSL_ISTIME_TYPE_LOGNORMAL:
		return "lognormal";
	default:
		return "none";
	}
}
